package dijjer.util;

import dijjer.util.logging.Logger;


public abstract class AbstractThread implements Runnable {

	public static final String RCSID
	    = "$Id: AbstractThread.java 2 2008-03-21 14:33:03Z ian.clarke $";

	private Thread _baseThread = null;
	private boolean _alive = true;
	private ThreadGroup _group = new ThreadGroup("Default Group");
	private boolean _killed = false;
	private boolean _shutdown = false;

	public void startThread() throws Exception {
		synchronized (this) {
			if (_baseThread == null) {
				_baseThread = new Thread(_group, this);
				_baseThread.setName(getClass().getName());
				init();
				_baseThread.start();
			}
		}
		Thread.yield();
	}

	protected void init() throws Exception {
	}

	public void shutdownThread() {
		Thread thread = null;
		synchronized (this) {
			thread = _baseThread;
		}
		if (thread != null) {
            _shutdown = true;
            Logger.info("Shutdown Thread:" + this.getClass().getName());
            thread.interrupt();
            shutdown();
            try {
                thread.join(2000);
            } catch (InterruptedException e) {
                Logger.warning("Got interrupt while waiting for process to shutdown, assuming all is well and breaking out.");
            }
			_baseThread = null;
		}
	}

	protected void shutdown() {
	}

	public final void run() {
		Logger.debug("Thread started: " + this);
		while (_alive && !_killed && !_shutdown) {
			try {
				_alive = loop();
			} catch (Throwable ex) {
				_alive = false;
				if (ex instanceof InterruptedException) {
					_killed = true;
				} else {
					Logger.error("Subclass process threw an exception", ex);
				}
			}
		}
		cleanUp();
		Logger.debug("Thread complete: " + this + ":" + _alive + ":" + _killed + ":" + _shutdown);
	}

	protected abstract boolean loop()
	    throws InterruptedException;

	protected void cleanUp() {};

	boolean isAlive() {
		return _baseThread != null && _baseThread.isAlive();
	}

	public Thread getBaseThread() {
		return _baseThread;
	}

	public void join() throws InterruptedException {
		Thread thread = null;
		synchronized (this) {
			thread = _baseThread;
		}
		if (thread != null) {
			thread.join();
		}
	}

	public boolean wasKilled() {
		return _killed;
	}

	public boolean wasShutdown() {
		return _shutdown;
	}
}
